//shader toy 是一个在线的着色器编辑器，可以在线编辑着色器代码，实时预览效果。
//下面是一些shader toy的常见内置变量和函数，内置变量和函数可以直接在shader toy中使用，不需要自己定义。
//uniform vec3 意思是从外部传入一个vec3类型的变量
//uniform float 意思是从外部传入一个float类型的变量
//uniform sampler2D 意思是从外部传入一个纹理
//uniform vec2 意思是从外部传入一个vec2类型的变量
//uniform vec3 iResolution; // 屏幕分辨率
//uniform float iTime; // 时间
//uniform float iTimeDelta; // 时间增量
//uniform int iFrame; // 帧数
//uniform float iChannelTime[4]; // 通道时间
//uniform vec3 iChannelResolution[4]; // 通道分辨率
//uniform vec4 iMouse; // 鼠标点击位置
//uniform sampler2D iChannel0; // 通道0纹理
//uniform sampler2D iChannel1; // 通道1纹理
//uniform sampler2D iChannel2; // 通道2纹理
//uniform sampler2D iChannel3; // 通道3纹理
//uniform float iDate; // 年月日时分
//uniform float iSampleRate; // 采样率
//uniform vec4 fragCoord; // 片元坐标
//uniform vec4 fragColor; // 片元颜色 
//uniform float fragDepth; // 片元深度

//mainImage是一个片元着色器函数，入参为out vec4 fragColor, in vec2 fragCoord，出参为片元颜色，
//这个函数是shader toy中的一个固定函数，shader toy会自动调用这个函数，所以我们只需要在这个函数中写我们的片元着色器代码即可。
//有没有其他的固定函数呢？有的，比如mainImage之前还有一个固定函数叫做main，这个函数是shader toy中的一个固定函数，shader toy会自动调用这个函数，所以我们只需要在这个函数中写我们的代码即可。
//顶点着色器函数是什么呢？名为vertexShader，入参为out vec4 fragColor, in vec2 fragCoord，出参为片元颜色，这个函数是shader toy中的一个固定函数，shader toy会自动调用这个函数，所以我们只需要在这个函数中写我们的顶点着色器代码即可。
//main函数和顶点着色器函数、片元着色器函数的关系是这样的，先调用main函数，然后调用顶点着色器函数，最后调用片元着色器函数。
//他们是同步进行的吗？是的，他们是同步进行的，也就是说main函数执行完毕之后，才会调用顶点着色器函数，顶点着色器函数执行完毕之后，才会调用片元着色器函数。
//main函数中通常写些什么代码呢？main函数中通常写一些初始化代码，比如设置背景颜色，设置纹理，设置一些uniform变量等。
//uniform变量是什么意思？uniform变量是从外部传入的变量，比如我们可以从外部传入一个纹理，一个颜色，一个时间等。
//还有其他变量吗？有的，比如我们可以定义一个局部变量，一个全局变量，一个常量等。
//如何从外部传入变量，以js为例怎么样传入？下面是具体的代码：

//如何自己定义一个uniform变量
//uniform vec3 bg_color; // 背景颜色
//自己定义的uniform变量，可以在shader toy中设置，也可以在代码中设置，在代码中设置的方法如下：
//uniform vec3 bg_color = vec3(0.0, 0.0, 0.0); // 背景颜色
//注意这里没有变量类型，直接使用等号赋值，这是因为uniform变量只能在shader toy中设置，不能在代码中设置，所以这里只是声明了一个变量，没有赋值。

// 归一化屏幕坐标系
vec2 ProjectionCoord(in vec2 fragCoord, in float scale) {
  //本函数实现将屏幕坐标转换为以画布中心为原点的坐标系，并进行归一化
  //首先当前画布左上角为原点，右下角为iResolution.xy，我们需要将其转换为以中心点为原点，
  vec2 centerCoord = fragCoord - 0.5 * iResolution.xy;
  //然后将坐标归一化, 这里需要取屏幕宽高的最小值，因为画布可能不是正方形，我们需要保证最小的一边长度为1，范围为(-0.5,0.5)
  vec2 normalizationCoord = centerCoord / min(iResolution.x, iResolution.y);
  //此时坐标系的范围是(-0.5,0.5), 为了让坐标系的范围是(-1,1), 我们再乘以2
  vec2 dobuleNormalizationCoord = 2. * normalizationCoord;
  //最后我们再乘以scale, 这样我们就可以控制坐标系的大小
  return scale * dobuleNormalizationCoord;
}

//坐标轴辅助对象，筛选出归属于坐标轴的片元，返回坐标轴颜色
vec4 AxisHelper(in vec2 coord, in float axisWidth, in vec4 xAxisColor, in vec4 yAxisColor) {
  //设置默认颜色为黑色透明度为0
  vec4 color = vec4(0.0);
  //这里dx为例，dFdx返回当前点的coord.x与相邻点的coord.x的差值，然后乘以axisWidth，得到一个屏幕上的一个片元在x方向上的长度
  // dFdx 的入参 必须是一个引用 而不能是一个具体的数值是吗，因为他要靠这个引用来查找相邻片源的该值
  //1个片元就是一个像素？不对，一个片元可以理解为一个逻辑上的像素，一个片元可以对应多个物理上的像素，比如一个片元可以对应4个像素，8个像素等。
  float dx = dFdx(coord.x) * axisWidth;
  float dy = dFdy(coord.y) * axisWidth;
  if(abs(coord.x) < dx) {
    color = xAxisColor;
  } else if(abs(coord.y) < dy) {
    color = yAxisColor;
  }
  return color;
}

//网格辅助对象，筛选出归属于网格的片元，返回网格颜色
vec4 GridHelper(in vec2 coord, in float gridWidth, in vec4 gridColor) {
  vec4 color = vec4(0, 0, 0, 0);
  float dx = dFdx(coord.x) * gridWidth;
  float dy = dFdy(coord.y) * gridWidth;
  //每单位长度的余数，如果说想要2个单位长度的网格，那么需要把coord.x和coord.y都除以2，然后取余数
  vec2 fraction = fract(coord);
  if(fraction.x < dx || fraction.y < dy) {
    color = gridColor;
  }
  return color;
} 

// 投影坐标系辅助对象
vec4 ProjectionHelper(in vec2 coord, in float axisWidth, in vec4 xAxisColor, in vec4 yAxisColor, in float gridWidth, in vec4 gridColor) {
  // 坐标轴
  vec4 axisHelper = AxisHelper(coord, axisWidth, xAxisColor, yAxisColor);
  // 栅格
  vec4 gridHelper = GridHelper(coord, gridWidth, gridColor);
  // =投影坐标系
  return bool(axisHelper.a) ? axisHelper : gridHelper;
}

void mainImage(out vec4 fragColor, in vec2 fragCoord) {
  //片元着色器代码,入参为片元坐标，出参为片元颜色
  vec2 uv = ProjectionCoord(fragCoord, 3.);
  fragColor = ProjectionHelper(uv, 1., vec4(1, 0, 0, 1), vec4(0, 1, 0, 1), 1., vec4(0.57, 0.57, 0.57, 1.0));
}
